import logging
logging.basicConfig(filename='/tmp/iswm.log', level=logging.DEBUG)

from ooxcb import xproto
from ooxcb.xproto import EventMask

from base import Object, Rect
from client import Client

def configure_request_to_dict(evt):
    cnf = {}
    mask = evt.value_mask
    
    if mask & xproto.ConfigWindow.X:
        cnf['x'] = evt.x
    if mask & xproto.ConfigWindow.Y:
        cnf['y'] = evt.y
    if mask & xproto.ConfigWindow.Width:
        cnf['width'] = evt.width
    if mask & xproto.ConfigWindow.Height:
        cnf['height'] = evt.height
    
    return cnf

class Screen(Object):
    client_class = Client
    
    def __init__(self, core, num):
        Object.__init__(self)
        self.core = core
        self.conn = core.conn
        self.clients = set()
        self.info = self.conn.get_setup().roots[num]
        self.root = self.info.root
        
        self.possible_states = dict((name, self.conn.atoms[name]) for name in (
            '_NET_WM_STATE_MODAL'
        ))
        
        self.root.change_attributes(
            event_mask=
                EventMask.SubstructureRedirect |
                EventMask.SubstructureNotify |
                EventMask.StructureNotify |
                EventMask.Exposure |
                EventMask.PropertyChange
        )
        
        self.root.push_handlers(self)
        
        self.set_supported_hints()
        self.check_window = self.create_check_window()
    
    def create_check_window(self):
        win = xproto.Window.create(self.conn,
            self.root,
            self.info.root_depth,
            self.info.root_visual,
            0, 0, 1, 1, 0,
            override_reditect=True)
        self.root.change_property('_NET_SUPPORTING_WM_CHECK', 'WINDOW', 32, [win.get_internal()])
        win.change_property('_NET_SUPPORTING_WM_CHECK', 'WINDOW', 32, [win.get_internal()])
        win.change_property('_NET_WM_NAME', 'UTF8_STRING', 8, map(ord, 'iswm'))
        
        return win
    
    def scan(self):
        children = self.root.query_tree().reply().children
        for child in children:
            logging.debug('%s found child %s', self, child)
            attr = child.get_attributes().reply()
            logging.debug('attr %s', attr)
            
            if attr.map_state != xproto.MapState.Viewable:
                logging.debug('%s not managing %s - not viewable', self, child)
                continue
            self.manage(child)
        
        self.layout_clients()
    
    def get_geometry(self):
        return Rect.from_object(self.root.get_geometry().reply())
    
    def manage(self, window):
        window.type = map(self.conn.atoms.get_by_id,
            window.get_property('_NET_WM_WINDOW_TYPE', 'ATOM').reply().value)
        
        attributes = window.get_attributes().reply()
        geom = window.get_geometry().reply()
        
        if attributes.override_redirect:
            logging.debug('%s not managing %s - override_redirect is set', self, window)
            return False
        
        client = self.client_class(self, window, geom)
        logging.debug('screen %s is now managing %s', self, client)
        self.clients.add(client)
        
        self.update_client_list()
        self.layout_clients()
        
        return client
    
    def unmanage(self, client):
        client.unmanage()
        self.update_client_list()
        self.clients.remove(client)
        
    def on_configure_request(self, evt):
        cnf = configure_request_to_dict(evt)
        if cnf:
            evt.window.configure_checked(**cnf).check()
        else:
            logging.log('Bad configure request')
    
    def on_map_request(self, evt):
        client = Client.get_by_window(evt.window)
        if client is None:
            if self.manage(evt.window):
                return
                
        evt.window.map()
    
    def on_client_removed(self, client):
        self.unmanage(client)
    
    def on_client_message(self, evt):
        logging.debug('Client message: %s' % evt)
    
    def on_key_press(self, evt):
        key = (evt.state, evt.detail)
        logging.debug('Key press: %s' % key)
    
    def set_supported_hints(self):
        """
            set the `_NET_SUPPORTED` atom to a bunch of atoms that
            we might not support yet, but in the future. Until then,
            let's hope that nobody notices that.
        """
        atoms = self.conn.atoms

        supported = [
            atoms['_NET_SUPPORTED'],
            atoms['_NET_CLIENT_LIST'],
            atoms['_NET_ACTIVE_WINDOW'],
            atoms['_NET_CLOSE_WINDOW'],

            atoms['_NET_WM_NAME'],
            atoms['_NET_WM_WINDOW_TYPE'],
            atoms['_NET_WM_WINDOW_TYPE_NORMAL'],
            atoms['_NET_WM_WINDOW_TYPE_DIALOG'],

            atoms['UTF8_STRING'],
        ]
        # We are not using the reparenting-to-a-fakeroot technique
        # described in the netwm standard,
        # so we don't need to set _NET_VIRTUAL_ROOTS.
        # TODO: ... do we need to set _NET_DESKTOP_LAYOUT? Are we a pager?
        self.root.change_property('_NET_SUPPORTED', 'ATOM', 32,
                [s.get_internal() for s in supported]) # TODO: nicer conversion
    
    def update_client_list(self):
        self.root.change_property('_NET_CLIENT.LIST',
                                  'WINDOW', 32,
                                  [c.window.get_internal() for c in self.client_class.all_clients])
    
    def layout_clients(self):
        logging.debug('Laying out %d clients' % len(self.clients))
        geom = self.get_geometry()
        i = 0
        uheight = geom.height // max(len(self.clients) - 1, 1)
        logging.debug('uheight: %d' % uheight)
        
        for client in self.clients:
            logging.debug('Laying out client: %s' % client)
            client.window.map()
            
            if i == 0:
                client.window.configure(x=0,
                    y=0,
                    width=geom.width // 2,
                    height=geom.height)
            else:
                client.window.configure(x=geom.width // 2,
                    y=(i - 1) * uheight,
                    width=geom.width // 2,
                    height = uheight - 1)
            i += 1
        
        self.conn.flush()

Screen.register_event_type('on_new_client')
